import { describe, test } from "bun:test";
import assert from "node:assert";
import url, { URL } from "node:url";

const isWindows = process.platform === "win32";

describe("url.fileURLToPath", () => {
  function testInvalidArgs(...args) {
    for (const arg of args) {
      assert.throws(() => url.fileURLToPath(arg), {
        code: "ERR_INVALID_ARG_TYPE",
      });
    }
  }

  // TODO: Support error code.
  test.todo("invalid input", () => {
    // Input must be string or URL
    testInvalidArgs(null, undefined, 1, {}, true);

    // Input must be a file URL
    assert.throws(() => url.fileURLToPath("https://a/b/c"), {
      code: "ERR_INVALID_URL_SCHEME",
    });

    const withHost = new URL("file://host/a");

    if (isWindows) {
      assert.strictEqual(url.fileURLToPath(withHost), "\\\\host\\a");
    } else {
      assert.throws(() => url.fileURLToPath(withHost), {
        code: "ERR_INVALID_FILE_URL_HOST",
      });
    }

    if (isWindows) {
      assert.throws(() => url.fileURLToPath("file:///C:/a%2F/"), {
        code: "ERR_INVALID_FILE_URL_PATH",
      });
      assert.throws(() => url.fileURLToPath("file:///C:/a%5C/"), {
        code: "ERR_INVALID_FILE_URL_PATH",
      });
      assert.throws(() => url.fileURLToPath("file:///?:/"), {
        code: "ERR_INVALID_FILE_URL_PATH",
      });
    } else {
      assert.throws(() => url.fileURLToPath("file:///a%2F/"), {
        code: "ERR_INVALID_FILE_URL_PATH",
      });
    }
  });

  test("general", () => {
    let testCases;
    if (isWindows) {
      testCases = [
        // Lowercase ascii alpha
        { path: "C:\\foo", fileURL: "file:///C:/foo" },
        // Uppercase ascii alpha
        { path: "C:\\FOO", fileURL: "file:///C:/FOO" },
        // dir
        { path: "C:\\dir\\foo", fileURL: "file:///C:/dir/foo" },
        // trailing separator
        { path: "C:\\dir\\", fileURL: "file:///C:/dir/" },
        // dot
        { path: "C:\\foo.mjs", fileURL: "file:///C:/foo.mjs" },
        // space
        { path: "C:\\foo bar", fileURL: "file:///C:/foo%20bar" },
        // question mark
        { path: "C:\\foo?bar", fileURL: "file:///C:/foo%3Fbar" },
        // number sign
        { path: "C:\\foo#bar", fileURL: "file:///C:/foo%23bar" },
        // ampersand
        { path: "C:\\foo&bar", fileURL: "file:///C:/foo&bar" },
        // equals
        { path: "C:\\foo=bar", fileURL: "file:///C:/foo=bar" },
        // colon
        { path: "C:\\foo:bar", fileURL: "file:///C:/foo:bar" },
        // semicolon
        { path: "C:\\foo;bar", fileURL: "file:///C:/foo;bar" },
        // percent
        { path: "C:\\foo%bar", fileURL: "file:///C:/foo%25bar" },
        // backslash
        { path: "C:\\foo\\bar", fileURL: "file:///C:/foo/bar" },
        // backspace
        { path: "C:\\foo\bbar", fileURL: "file:///C:/foo%08bar" },
        // tab
        { path: "C:\\foo\tbar", fileURL: "file:///C:/foo%09bar" },
        // newline
        { path: "C:\\foo\nbar", fileURL: "file:///C:/foo%0Abar" },
        // carriage return
        { path: "C:\\foo\rbar", fileURL: "file:///C:/foo%0Dbar" },
        // latin1
        { path: "C:\\fóóbàr", fileURL: "file:///C:/f%C3%B3%C3%B3b%C3%A0r" },
        // Euro sign (BMP code point)
        { path: "C:\\€", fileURL: "file:///C:/%E2%82%AC" },
        // Rocket emoji (non-BMP code point)
        { path: "C:\\🚀", fileURL: "file:///C:/%F0%9F%9A%80" },
        // UNC path (see https://docs.microsoft.com/en-us/archive/blogs/ie/file-uris-in-windows)
        { path: "\\\\nas\\My Docs\\File.doc", fileURL: "file://nas/My%20Docs/File.doc" },
      ];
    } else {
      testCases = [
        // Lowercase ascii alpha
        { path: "/foo", fileURL: "file:///foo" },
        // Uppercase ascii alpha
        { path: "/FOO", fileURL: "file:///FOO" },
        // dir
        { path: "/dir/foo", fileURL: "file:///dir/foo" },
        // trailing separator
        { path: "/dir/", fileURL: "file:///dir/" },
        // dot
        { path: "/foo.mjs", fileURL: "file:///foo.mjs" },
        // space
        { path: "/foo bar", fileURL: "file:///foo%20bar" },
        // question mark
        { path: "/foo?bar", fileURL: "file:///foo%3Fbar" },
        // number sign
        { path: "/foo#bar", fileURL: "file:///foo%23bar" },
        // ampersand
        { path: "/foo&bar", fileURL: "file:///foo&bar" },
        // equals
        { path: "/foo=bar", fileURL: "file:///foo=bar" },
        // colon
        { path: "/foo:bar", fileURL: "file:///foo:bar" },
        // semicolon
        { path: "/foo;bar", fileURL: "file:///foo;bar" },
        // percent
        { path: "/foo%bar", fileURL: "file:///foo%25bar" },
        // backslash
        { path: "/foo\\bar", fileURL: "file:///foo%5Cbar" },
        // backspace
        { path: "/foo\bbar", fileURL: "file:///foo%08bar" },
        // tab
        { path: "/foo\tbar", fileURL: "file:///foo%09bar" },
        // newline
        { path: "/foo\nbar", fileURL: "file:///foo%0Abar" },
        // carriage return
        { path: "/foo\rbar", fileURL: "file:///foo%0Dbar" },
        // latin1
        { path: "/fóóbàr", fileURL: "file:///f%C3%B3%C3%B3b%C3%A0r" },
        // Euro sign (BMP code point)
        { path: "/€", fileURL: "file:///%E2%82%AC" },
        // Rocket emoji (non-BMP code point)
        { path: "/🚀", fileURL: "file:///%F0%9F%9A%80" },
      ];
    }

    for (const { path, fileURL } of testCases) {
      const fromString = url.fileURLToPath(fileURL);
      assert.strictEqual(fromString, path);
      const fromURL = url.fileURLToPath(new URL(fileURL));
      assert.strictEqual(fromURL, path);
    }
  });
});
